package de.tu_darmstadt.gdi1.towerdefense.controller;

import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.Vector;

import javax.swing.JButton;

import de.tu_darmstadt.gdi1.towerdefense.model.TowerDefenseModel;
import de.tu_darmstadt.gdi1.towerdefense.test.adapter.TowerDefenseTestAdapterExtended2;
import de.tu_darmstadt.gdi1.towerdefense.ui.StudentWindow;

/**
 * 
 *
 * 
 */
public class TowerDefenseController extends Thread implements
		TowerDefenseTestAdapterExtended2, MouseListener, ActionListener {

	/**
	 * the view instance
	 */
	private StudentWindow view;
	/**
	 * the model instance
	 */
	private TowerDefenseModel model;
	/**
	 * the time in milliseconds for one game tick
	 */
	private long timeForOneGameTick;
	/**
	 * the current mode of the controllers build command
	 */
	private Modes mode;

	/**
	 * 
	 * the possible build modes
	 * 
	 */
	private enum Modes {
		DAMAGE_TOWER, SNIPER_TOWER, DESTROY_TOWER, UPGRADE_TOWER, NEUTRAL
	}

	/**
	 * constructor
	 * 
	 * @param timeForGameTick
	 *            the time in milliseconds for one game tick
	 */
	public TowerDefenseController(long _timeForGameTick) {
		timeForOneGameTick = _timeForGameTick;
		mode = Modes.NEUTRAL;
		model = new TowerDefenseModel();
		view = new StudentWindow("Tower Defense Gruppe 39");
		view.addMenuListeners(this);
		view.getGamePanel().addMouseListener(this);
		view.addModel(model);
		view.getTowerFrame().addListener(this);
		view.setVisible(true);
		model.addObserver(view);
	}

	/**
	 * starts the game loop
	 */
	public void startGame() {
		while (!model.gameIsWon() && !model.gameIsLost()) {
			// setup timer for the game round
			long beginRoundTime = System.currentTimeMillis();
			tick();
			try {
				if (timeForOneGameTick
						- (System.currentTimeMillis() - beginRoundTime) > 0)
					Thread.sleep((timeForOneGameTick - (System
							.currentTimeMillis() - beginRoundTime)));
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		if (model.gameIsLost())
			view.gameIsLost();
		if (model.gameIsWon())
			view.gameIsWon();
	}

	/**
	 * @param args
	 *            jump in point for the controller
	 */
	public static void main(String[] args) {

		TowerDefenseController controller = new TowerDefenseController(600);
		controller.view.getGamePanel().addControllerListener(controller);

		controller.model
				.createMap("src/de/tu_darmstadt/gdi1/resources/levels/level_02.txt");

		controller.model.addMonsterSpawner(5, 5);
		controller.view.getPlayerFrame().addObserverToObservables();
		controller.startGame();
	}

	@Override
	/**
	 * resets the game model
	 */
	public void reset() {
		model.reset();

	}

	/**
	 * load a level from a file or a string
	 */
	@Override
	public void loadLevelFromString(String level) {
		model.createMap(level);
	}

	/**
	 * returns a string representation of the current map
	 * 
	 * @return the level as string
	 */
	@Override
	public String levelToString() {
		StringBuffer sb = new StringBuffer();
		char[][] tmplvl = model.getCurrentMap();

		for (int i = 0; i < tmplvl.length; i++) {
			for (int j = 0; j < tmplvl[i].length; j++) {
				sb.append(tmplvl[i][j]);
			}
			sb.append("\n");
		}

		return sb.toString();

	}

	/**
	 * if the level loaded is valid
	 * 
	 * @return if the level is valid
	 */
	@Override
	public boolean levelIsValid() {
		return model.levelIsValid();
	}

	/**
	 * @return the level element at the given position
	 */
	@Override
	public char levelElementAt(int x, int y) {
		return model.getCurrentMap()[y][x];
	}

	/**
	 * @return the current player health
	 */
	@Override
	public int getPlayerHealth() {
		return model.getPlayerHealth();
	}

	/**
	 * places a tower at a given position
	 */
	@Override
	public void placeTower(int x, int y) {
		model.buildDamageTower(new Point(x, y));
	}

	/**
	 * @return if a tower is build at the given position
	 */
	@Override
	public boolean isTowerAt(int x, int y) {
		return model.isTowerAt(x, y);
	}

	/**
	 * spawn a monster at the given position
	 */
	@Override
	public void spawnMonster(int x, int y) {
		model.spawnMonster(x, y);
	}

	/**
	 * @return if a monster is at the given position
	 */
	@Override
	public boolean isMonsterAt(int x, int y) {
		if (model.getMonstersAt(x, y) > 0)
			return true;
		return false;
	}

	/**
	 * @return currently spawned monsters
	 */
	@Override
	public int monsterCount(int x, int y) {
		return model.getMonstersAt(x, y);
	}

	/**
	 * process all the steps of the game loop
	 */
	@Override
	public void tick() {
		model.wakeMonsters();
		model.allTowersShoot();
		model.allMonsterMove();
		model.finalizeRound();
	}

	/**
	 * process an entering action event
	 */
	@Override
	public void actionPerformed(ActionEvent arg0) {
		if (arg0.getActionCommand().equals("Build Damage Tower"))
			mode = Modes.DAMAGE_TOWER;
		if (arg0.getActionCommand().equals("Destroy Tower"))
			mode = Modes.DESTROY_TOWER;
		if (arg0.getActionCommand().equals("Upgrade Tower"))
			mode = Modes.UPGRADE_TOWER;
		if (arg0.getActionCommand().equals("Build Sniper Tower"))
			mode = Modes.SNIPER_TOWER;
	}

	@Override
	/**
	 * element of the gamepanel copied into the controller to process mouse events
	 */
	public void mouseClicked(MouseEvent evt) {
		if (!view.getGamePanel().hasEntities())
			return;
		// retrieve first button
		Vector<JButton> entities = view.getGamePanel().getEntities();
		JButton refBtn = entities.get(0);

		// iterate buttons until right one was found
		for (int i = 0; i < entities.size(); i++) {
			JButton btn = entities.get(i);
			if (evt.getSource() == btn) {
				// determine x and y position
				int posX = evt.getXOnScreen();
				posX = posX
						- (int) view.getGamePanel().getLocationOnScreen()
								.getX();

				int posY = evt.getYOnScreen();
				posY = posY
						- (int) view.getGamePanel().getLocationOnScreen()
								.getY();

				if (mode.equals(Modes.DAMAGE_TOWER))
					model.buildDamageTower(new Point(posX / refBtn.getWidth(),
							posY / refBtn.getHeight()));

				if (mode.equals(Modes.DESTROY_TOWER))
					model.destroyTower(new Point(posX / refBtn.getWidth(), posY
							/ refBtn.getHeight()));

				if (mode.equals(Modes.UPGRADE_TOWER))
					model.upgradeTowerAtCoordinate(new Point(posX
							/ refBtn.getWidth(), posY / refBtn.getHeight()));

				if (mode.equals(Modes.SNIPER_TOWER))
					model.buildSniperTower(new Point(posX / refBtn.getWidth(),
							posY / refBtn.getHeight()));

				// done!
				evt.consume();
				break;
			}
		}
	}

	/**
	 * unused
	 */
	@Override
	public void mouseEntered(MouseEvent arg0) {

	}

	/**
	 * unused
	 */
	@Override
	public void mouseExited(MouseEvent arg0) {

	}

	/**
	 * unused
	 */
	@Override
	public void mousePressed(MouseEvent arg0) {

	}

	/**
	 * unused
	 */
	@Override
	public void mouseReleased(MouseEvent arg0) {

	}

	/**
	 * places a tower at the given position and reduces the players funds by the
	 * towers cost
	 */
	@Override
	public void buyAndPlaceDefaultTowerTo(Point coordinates) {
		model.buildDamageTower(coordinates);

	}

	/**
	 * set the players money to the given amount
	 */
	@Override
	public void setMoney(double value) {
		model.setMoney(value);
	}

	/**
	 * @return the players current money
	 */
	@Override
	public double getMoney() {
		return model.getPlayerMoney();
	}

	/**
	 * sell the tower at the given position
	 */
	@Override
	public void sellTowerAtCoordinate(Point position) {
		model.destroyTower(position);

	}

	/**
	 * upgrade a tower at the given position
	 */
	@Override
	public void upgradeTowerAtCoordinate(Point position) {
		model.upgradeTowerAtCoordinate(position);
	}

}
